/* 
 * Advanced Foundation Classes
 * Copyright (C) 2000/2004  Fabio Rotondo - Andrea Galimberti
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "xmlparser.h"

static const char class_name[] = "XmlParser";


/*
@node afc_xmlparser_new

	         NAME: afc_xmlparser_new - Initializes a new afc_xmlparser instance

	     SYNOPSIS: XmlParser * afc_xmlparser_new ()

	  DESCRIPTION: This function initializes a new afc_xmlparser instance.

	        INPUT: NONE.

	      RESULTS: a valid inizialized afc_xmlparser structure. NULL in case of errors.

	     SEE ALSO: - afc_xmlparser_delete()
	               - afc_xmlparser_clear()

@endnode
*/
XmlParser * afc_xmlparser_new ()
{
	XmlParser * xmlparser = ( XmlParser * ) malloc ( sizeof ( XmlParser ) );
	if ( xmlparser == NULL ) return ( NULL );
	memset ( xmlparser, 0, sizeof ( XmlParser ) );
	xmlparser->magic = AFC_XMLPARSER_MAGIC;

	xmlparser->state = ( XmlParserState * ) malloc ( sizeof ( XmlParserState ) );
	if ( xmlparser->state == NULL ) {
		afc_xmlparser_delete ( xmlparser );
		return ( NULL );
	}
	memset ( xmlparser->state, 0, sizeof ( XmlParserState ) );
	xmlparser->state->xmlparser = xmlparser;  /* !! */

	xmlparser->callBacks = afc_dictionary_new ();
	if ( xmlparser->callBacks == NULL ) {
		afc_xmlparser_delete ( xmlparser );
		return ( NULL );
	}

	xmlparser->entities = afc_dictionary_new ();
	if ( xmlparser->entities == NULL ) {
		afc_xmlparser_delete ( xmlparser );
		return ( NULL );
	}

	xmlparser->element_attrs = afc_dictionary_new ();
	if ( xmlparser->element_attrs == NULL ) {
		afc_xmlparser_delete ( xmlparser );
		return ( NULL );
	}

	xmlparser->sax_handler = ( xmlSAXHandlerPtr ) malloc ( sizeof ( xmlSAXHandler ) );
	if ( xmlparser->sax_handler == NULL ) {
		afc_xmlparser_delete ( xmlparser );
		return ( NULL );
	}
	memset ( xmlparser->sax_handler, 0, sizeof ( xmlSAXHandler ) );
	xmlparser->sax_handler->startElement = ( startElementSAXFunc ) afc_xmlparser_internal_startElement;
	xmlparser->sax_handler->endElement = ( endElementSAXFunc ) afc_xmlparser_internal_endElement;
	xmlparser->sax_handler->startDocument = ( startDocumentSAXFunc ) afc_xmlparser_internal_startDocument;
	xmlparser->sax_handler->endDocument = ( endDocumentSAXFunc ) afc_xmlparser_internal_endDocument;
	xmlparser->sax_handler->characters = ( charactersSAXFunc ) afc_xmlparser_internal_characters;
	xmlparser->sax_handler->getEntity = ( getEntitySAXFunc ) afc_xmlparser_internal_entity_handler;
	//xmlparser->sax_handler->entityDecl = ( entityDeclSAXFunc ) afc_xmlparser_internal_entityDecl;
	//xmlparser->sax_handler->reference = ( referenceSAXFunc ) afc_xmlparser_internal_reference;
	xmlparser->sax_handler->warning = ( warningSAXFunc ) afc_xmlparser_internal_warning;
	xmlparser->sax_handler->error = ( errorSAXFunc ) afc_xmlparser_internal_error;
	xmlparser->sax_handler->fatalError = ( fatalErrorSAXFunc ) afc_xmlparser_internal_fatal_error;

	xmlparser->tagUnknown = NULL;

	return ( xmlparser );
}


/*
@node afc_xmlparser_delete

	         NAME: afc_xmlparser_delete - Disposes a valid xmlparser instance.

	     SYNOPSIS: int afc_xmlparser_delete ( XmlParser * xmlparser)

	  DESCRIPTION: This function frees an already allocated afc_xmlparser structure.

	        INPUT: - xmlparser - pointer to a valid afc_xmlparser instance

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_INVALID_POINTER - if the xmlparser pointer doesn't
	                 point to a valid XmlParser instance
	               - AFC_XMLPARSER_ERR_NO_ERROR - if no error occurred

	        NOTES: - this method calls: afc_xmlparser_clear()

	     SEE ALSO: - afc_xmlparser_new()
	               - afc_xmlparser_clear()

*/
int afc_xmlparser_delete ( XmlParser * xmlparser ) 
{
	int afc_res; 
	if ( ( afc_res = afc_xmlparser_clear ( xmlparser ) ) != AFC_XMLPARSER_ERR_NO_ERROR ) return ( afc_res );

	/* NOTE: any class contained in afc_xmlparser should be deleted here */

	if ( xmlparser->sax_handler ) free ( xmlparser->sax_handler );
	if ( xmlparser->state ) free ( xmlparser->state );
	if ( xmlparser->callBacks ) afc_dictionary_delete ( xmlparser->callBacks );
	if ( xmlparser->entities ) afc_dictionary_delete ( xmlparser->entities );
	if ( xmlparser->element_attrs ) afc_dictionary_delete ( xmlparser->element_attrs );
	free ( xmlparser );

	return ( AFC_XMLPARSER_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_clear

	         NAME: afc_xmlparser_clear - Clears the XmlParser instance

	     SYNOPSIS: int afc_xmlparser_clear ( XmlParser * xmlparser )

	  DESCRIPTION: Use this function to clear all stored data in the current XmlParser instance.

	        INPUT: - xmlparser - pointer to a valid afc_xmlparser instance

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_INVALID_POINTER - if the xmlparser pointer doesn't
	                 point to a valid XmlParser instance
	               - AFC_XMLPARSER_ERR_NO_ERROR - if no error occurred

	     SEE ALSO: afc_xmlparser_delete()
	               
@endnode

*/
int afc_xmlparser_clear ( XmlParser * xmlparser ) 
{
	XmlParserCallbackTuple * ct = NULL;
	xmlEntity * en = NULL;

	if ( xmlparser == NULL ) return ( AFC_XMLPARSER_ERR_NULL_POINTER );
	if ( xmlparser->magic != AFC_XMLPARSER_MAGIC ) return ( AFC_XMLPARSER_ERR_INVALID_POINTER );


	/* Custom Clean-up code should go here */

	ct = afc_dictionary_first ( xmlparser->callBacks );
	while ( ct != NULL ) {
		free ( ct );
		ct = afc_dictionary_next ( xmlparser->callBacks );
	}
	afc_dictionary_clear ( xmlparser->callBacks );

	en = afc_dictionary_first ( xmlparser->entities );
	while ( en != NULL ) {
		afc_xmlparser_internal_entity_del ( en );
		en = afc_dictionary_next ( xmlparser->entities );
	}
	afc_dictionary_clear ( xmlparser->entities );

	afc_xmlparser_internal_attributes_clear ( xmlparser );

	return ( AFC_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_parse_file

	         NAME: afc_xmlparser_parse_file - Starts the parsing of an XML file

	     SYNOPSIS: int afc_xmlparser_parse_file ( XmlParser * xmlparser,  char * filename, void * userdata )

	  DESCRIPTION: start the parsing of the XML file named "filename". This function takes
	               three parameters: the pointer to a valid XmlParser instance, the name of
	               the file to process and the pointer to some custom data.
	               The "userdata" parameter is passed to every callback called during the
	               parsing of the file: thus it usually points to a structure containing some
	               information related to the current state of the parser, but you can put in it
	               everything you like.

	        INPUT: - xmlparser - pointer to a valid XmlParser instance
	               - filename  - name of the file to process
	               - userdata  - pointer to some custom data to be passed to the callbacks

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_NO_CTXT   - if the SAX parser's context cannot be created
	               - AFC_XMLPARSER_ERR_BADFORMED - if the XML document parsed is not well formed
	               - AFC_XMLPARSER_ERR_NO_ERROR  - if all went ok

	     SEE ALSO: afc_xmlparser_add_callback()

@endnode
*/
int afc_xmlparser_parse_file ( XmlParser * xmlparser,  char * filename, void * userdata )
{
	int res = 0;

	if ( xmlparser == NULL ) return ( AFC_XMLPARSER_ERR_NULL_POINTER );
/*
	xmlparser->sax_context = xmlCreateFileParserCtxt(filename);
	if ( xmlparser->sax_context == NULL ) return AFC_XMLPARSER_ERR_NO_CTXT;
	xmlparser->sax_context->sax = xmlparser->sax_handler;
	xmlparser->state->userdata = userdata;
	xmlparser->sax_context->userData = xmlparser->state;

	// xmlparser->sax_context->myDoc = xmlNewDoc ( xmlparser->sax_context->version );

	xmlParseDocument ( xmlparser->sax_context );
	if ( xmlparser->sax_context->wellFormed )
		res = AFC_XMLPARSER_ERR_NO_ERROR;
	else
		res = AFC_XMLPARSER_ERR_BADFORMED;

	if ( xmlparser->sax_context->sax != NULL ) xmlparser->sax_context->sax = NULL;
	xmlFreeParserCtxt ( xmlparser->sax_context );
	xmlparser->sax_context = NULL;
*/

	xmlparser->state->userdata = userdata;
	res = xmlSAXUserParseFile ( xmlparser->sax_handler, xmlparser->state, filename);

	return res;
}

/*
@node afc_xmlparser_add_callback

	         NAME: afc_xmlparser_add_callback - Adds the callbacks associated with an element

	     SYNOPSIS: int afc_xmlparser_add_callback ( XmlParser * xmlparser, char * name, BeginCallbackFunc beginCb, EndCallbackFunc endCb )

	  DESCRIPTION: adds the callbacks associated with an element. To each element contained in
	               your XML file you can associate two callbacks: the first is called when the
	               starting tag is encountered, the second when the ending tag is found.
	               The callback linked to the start tag must have the prototype:

	               void startCallback ( void * userdata, Dictionary * attrs )

	               The callback linked to the end tag must have the prototype:

	               void endCallback ( void * userdata )

	               The "userdata" parameter points to the custom data passed to the
	               afc_xmlparser_parse_file() function, while the "attrs" parameter is a pointer
	               to the Dictionary object containing the start tag's attributes: the key of a
                 Dictionary's element is the name of the attribute, while the associated value is
                 the value of the attribute.

	               If the element "name" is already associated with callbacks, then the new
	               callbacks replace the old ones.

	               The afc_xmlparser_set_callback() function allows you to set a
	               particular type of callback that will be called every time an "unknown" tag
	               is encountered ("unknown" means that it has no callbacks associated).

	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.

	        INPUT: - xmlparser - a valid XmlParser instance
	               - name    - name of the element associated with the callbacks
	               - beginCb - pointer to the callback associated with the element's starting tag
	               - endCb   - pointer to the callback associated with the element's ending tag

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_NO_MEMORY - if the procedure didn't succeed in adding the
	                 callbacks
	               - AFC_XMLPARSER_ERR_NO_ERROR  - if all went ok

	     SEE ALSO: - afc_xmlparser_parse_file()
	               - afc_xmlparser_set_callback()
	               - afc_xmlparser_del_callback()

@endnode
*/
int afc_xmlparser_add_callback ( XmlParser * xmlparser, char * name, BeginCallbackFunc beginCb, EndCallbackFunc endCb )
{
	XmlParserCallbackTuple * ct = NULL;

	if ( xmlparser == NULL ) return ( AFC_XMLPARSER_ERR_NULL_POINTER );

	if ( afc_dictionary_has_key ( xmlparser->callBacks, name ) )
		ct = ( XmlParserCallbackTuple * ) afc_dictionary_get ( xmlparser->callBacks, name );
	else {
		ct = ( XmlParserCallbackTuple * ) malloc ( sizeof ( XmlParserCallbackTuple ) );
		if ( ct == NULL ) return ( AFC_XMLPARSER_ERR_NO_MEMORY );
		memset ( ct, 0, sizeof ( XmlParserCallbackTuple ) );
	}

	ct->beginCallback = beginCb;
	ct->endCallback = endCb;
	afc_dictionary_set ( xmlparser->callBacks, name, ct );

	return ( AFC_XMLPARSER_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_del_callback

	         NAME: afc_xmlparser_del_callback - Removes the callbacks associated with an element

	     SYNOPSIS: int afc_xmlparser_del_callback ( XmlParser * xmlparser, char * name )

	  DESCRIPTION: removes the callbacks associated with an element. If the element has no
	               associated callbacks, then an error is returned. The "name" parameter
	               will contain the name of the element.

	        INPUT: - xmlparser - pointer to a valid XmlParser instance
	               - name - name of the element whose callbacks must be removed

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_UNKNOWN_CALLBACK - if the element has no associated
	                 callbacks
	               - AFC_XMLPARSER_ERR_NO_ERROR - if all went ok

	     SEE ALSO: - afc_xmlparser_add_callback()
	               - afc_xmlparser_set_callback()

@endnode
*/
int afc_xmlparser_del_callback ( XmlParser * xmlparser, char * name )
{
	if ( xmlparser == NULL ) return ( AFC_XMLPARSER_ERR_NULL_POINTER );

	if ( afc_dictionary_has_key ( xmlparser->callBacks, name ) ) {
		afc_dictionary_del_item ( xmlparser->callBacks, name );
	}
	else return ( AFC_XMLPARSER_ERR_UNKNOWN_CALLBACK );

	return ( AFC_XMLPARSER_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_set_callback

	         NAME: afc_xmlparser_set_callback - Sets some special callbacks

	     SYNOPSIS: int afc_xmlparser_set_callback ( XmlParser * xmlparser, int cbtype, void ( * cback ) )

	  DESCRIPTION: sets some special callbacks. This function is used to set some callbacks
	               not associated with a tag: every callback is identificated by means of a
	               constant that is passed as the value of the cbtype parameter. The possible
	               types of callback are the following:

	               - AFC_XMLPARSER_CBTYPE_TAG_UNKNOWN - this callback is called every time an
	                 "unknown" tag is found: "unknown" means that the tag hasn't been associated with
	                 any callback using the afc_xmlparser_add_callback() function; the prototype of
	                 the function to be passed in the cback parameter is:

	                 void tag_unknown ( void * userdata, const xmlChar * name, Dictionary * attrs )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function, "name" is the name of the (unknown) tag found, "attrs" is a Dictionary
	                 containing the tag's attributes (see afc_xmlparser_add_callback() for a description).

	               - AFC_XMLPARSER_CBTYPE_START_DOC - called when the parser opens the document and
	                 before any actual parsing begins: this callback is often used to perform
	                 some initializations; the prototype of the function to be passed in the cback
	                 parameter is:

	                 void start_document ( void * userdata )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function.
 
	               - AFC_XMLPARSER_CBTYPE_END_DOC - called when the parser has finished parsing the
	                 document: this callback is often used to perform some finalizations; the
	                 prototype of the function to be passed in the cback parameter is:

	                 void end_document ( void * userdata )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function. 

	               - AFC_XMLPARSER_CBTYPE_CHARACTERS - called whenever a character that is outside
	                 any tag is encountered. This is the far most important callback: it is used to
	                 store the data enclosed between a pair of start and end tags; the prototype of
	                 the function to be passed in the cback parameter is:

	                 void handle_data ( void * userdata, const xmlChar * data, int len )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function, "data" is a buffer containing the characters read, "len" is the
	                 number of characters in the buffer. Note that the buffer is not necessarily
	                 null-terminated, and that this callback may be called many times to read
	                 all the characters contained between a start and end tag.

	               - AFC_XMLPARSER_CBTYPE_WARNING - called whenever a warning is issued by the parser.
	                 The prototype of the function to be passed in the cback parameter is:

	                 void warning ( void * userdata, const char * msg )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function and "msg" is the message describing the warning.

	               - AFC_XMLPARSER_CBTYPE_ERROR - called whenever an error is issued by the parser.
	                 The prototype of the function to be passed in the cback parameter is:

	                 void error ( void * userdata, const char * msg )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function and "msg" is the message describing the error.

								 - AFC_XMLPARSER_CBTYPE_FATAL_ERROR - called whenever a fatal error is issued by
									 the parser.
	                 The prototype of the function to be passed in the cback parameter is:

	                 void fatal_error ( void * userdata, const char * msg )

	                 where "userdata" is the custom data passed to the afc_xmlparser_parse_file()
	                 function and "msg" is the message describing the fatal error.


	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.
 
								 To unset any of the above callbacks simply pass NULL as the value of the
	               "cback" parameter.

	        INPUT: - xmlparser - pointer to a valid XmlParser instance
	               - cbtype    - type of callback to set
	               - cback     - pointer to the function containing the actions to perform

	      RESULTS: - AFC_XMLPARSER_ERR_NULL_POINTER - if the xmlparser pointer is null
	               - AFC_XMLPARSER_ERR_NO_ERROR     - if all went ok

	     SEE ALSO: - afc_xmlparser_add_callback()
	               - afc_xmlparser_del_callback()
 
@endnode
*/
int afc_xmlparser_set_callback ( XmlParser * xmlparser, int cbtype, void ( * cback ) )
{
	if ( xmlparser == NULL ) return ( AFC_XMLPARSER_ERR_NULL_POINTER );

	switch ( cbtype ) {
		case AFC_XMLPARSER_CBTYPE_TAG_UNKNOWN:
			xmlparser->tagUnknown = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_START_DOC:
			xmlparser->startDocument = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_END_DOC:
			xmlparser->endDocument = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_CHARACTERS:
			xmlparser->characters = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_WARNING:
			xmlparser->warning = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_ERROR:
			xmlparser->error = cback;
			break;

		case AFC_XMLPARSER_CBTYPE_FATAL_ERROR:
			xmlparser->fatalError = cback;
			break;
	}

	return ( AFC_XMLPARSER_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_entity_set

	         NAME: afc_xmlparser_entity_set - Associates an entity to its content 

	     SYNOPSIS: int afc_xmlparser_entity_set ( XmlParser * xmlparser, char * name, int type, char * extID, char * sysID, char * content )

	  DESCRIPTION: associates the name of an entity to its content. Remember that you have to call this method
	               for each entity that may appear in your document *before* the document is loaded and parsed,
	               otherwise if the parser encounters an unknown entity (that is to say an entity not previously
	               set with this function) it exits signalling an invalid document.
	               Usually this function is called giving the following default values:
	               - type = 0
	               - extID = NULL
	               - sysID = NULL
	               while the "name" parameter contains the name of the entity, and the "content" parameter contains the
	               text to be substituted for the entity.

	        INPUT: - xmlparser - pointer to a valid XmlParser instance
	               - name - name of the entity
	               - type - type of entity; can be one of:
	                 + XML_INTERNAL_GENERAL_ENTITY
	                 + XML_EXTERNAL_GENERAL_PARSED_ENTITY
	                 + XML_EXTERNAL_GENERAL_UNPARSED_ENTITY
	                 + XML_INTERNAL_PARAMETER_ENTITY
	                 + XML_EXTERNAL_PARAMETER_ENTITY
	                 + XML_INTERNAL_PREDEFINED_ENTITY
	                 If you pass 0 as the value, the XmlParser sets the type to XML_INTERNAL_PREDEFINED_ENTITY.
	               - extID - external identifier for PUBLIC
	               - sysID - URI for a SYSTEM or PUBLIC entity
	               - content - text the entity should be replaced with

	      RESULTS: - AFC_ERR_NO_MEMORY - if it couldn't allocate the memory for the entity
	               - AFC_ERR_NO_ERROR  - otherwise

@endnode
*/
int afc_xmlparser_entity_set ( XmlParser * xmlparser, char * name, int type, char * extID, char * sysID, char * content )
{
	xmlEntity * en = NULL;

	if ( ( en = ( xmlEntity * ) afc_dictionary_get ( xmlparser->entities, name ) ) == NULL ) {
	  if ( ( en = ( xmlEntity * ) malloc ( sizeof ( xmlEntity ) ) ) == NULL )
			return AFC_LOG_FAST_INFO ( AFC_ERR_NO_MEMORY, "entity" );

		memset ( en, 0, sizeof ( xmlEntity ) );
		afc_dictionary_set ( xmlparser->entities, name, en );
	}

	en->type = XML_ENTITY_DECL;
	en->name = xmlCharStrdup ( name );
	en->etype = ( type == 0 ) ? XML_INTERNAL_PREDEFINED_ENTITY : type ;
	if ( extID != NULL ) en->ExternalID = extID;
	if ( sysID != NULL ) en->SystemID = sysID;

	//en->orig = xmlCharStrdup ( "pippo" );
	en->content = xmlCharStrdup ( content );
	en->length = xmlStrlen ( en->content );

	return ( AFC_ERR_NO_ERROR );
}

/* buffer management functions */

/*
@node afc_xmlparser_buffer_append

       NAME: afc_xmlparser_buffer_append - Appends some data to a buffer (or creates a new one)

   SYNOPSIS: int afc_xmlparser_buffer_append ( char ** buffer_ptr, const xmlChar * data, int len )

DESCRIPTION: this function creates a new buffer or extends an existing buffer and copies into it the
             contents of the "data" parameter.
             Note that the first parameter is the *pointer* to the buffer address: if the buffer address is
             NULL, then this function allocates a new buffer, otherwise it uses the specified buffer.
             If the buffer is too small, then it is extended before appending the contents of "data": the
             pointer to the new (bigger) buffer is copied in the buffer_ptr parameter, thus
             buffer_ptr always contains a valid address.
             The "len" parameter is the number of characters of "data" to be added to the buffer.

             Note: a buffer created with this function must be disposed of with the afc_xmlparser_buffer_delete()
                   function.

     INPUTS: - buffer_ptr - pointer to the pointer to the buffer, or NULL if a new buffer should be created;
             - data - data to be appended to the buffer: the type is xmlChar because libxml2 functions use
                      strings of xmlChars. This function performs the conversion to char and then appends the
                      result to the buffer;
             - len - length (in characters) of the data to be added to the buffer.

    RESULTS: - AFC_ERR_NO_MEMORY - if the buffer cannot be allocated or extended;
             - AFC_ERR_NO_ERROR otherwise.

   SEE ALSO: - afc_xmlparser_buffer_clear()
             - afc_xmlparser_buffer_delete()
@endnode
*/
int afc_xmlparser_buffer_append ( char ** buffer_ptr, const xmlChar * data, int len )
{
	char * buffer = *buffer_ptr;
	char * new_buffer = NULL;

	if ( len == 0 ) return ( AFC_ERR_NO_ERROR );

	if ( buffer == NULL ) {
		/* DO NOT replace the sequence afc_string_new and afc_string_copy with afc_string_dup, because it doesn't
		   work for an xmlChar string! We have to use the "len" parameter. */
		if ( ( buffer = afc_string_new ( len ) ) == NULL )
			return AFC_LOG_FAST_INFO ( AFC_ERR_NO_MEMORY, "Cannot allocate buffer" );
		afc_string_copy ( buffer, data, len );
		*buffer_ptr = buffer;
	}
	else {
		if ( ( afc_string_len ( buffer ) + len ) > afc_string_max ( buffer ) ) {
			new_buffer = afc_string_new ( afc_string_max ( buffer ) * 2 + len );
			if ( new_buffer == NULL ) return AFC_LOG_FAST_INFO ( AFC_ERR_NO_MEMORY, "Cannot extend buffer" );
			/* The contents of new_buffer MUST be created with the sequence afc_string_copy and afc_string_add: these
			   functions CANNOT be replaced with afc_string_make, because we have to use the "len" parameter
			   that libxml2 passes to this function */
			afc_string_copy ( new_buffer, buffer, ALL );
			afc_string_add ( new_buffer, data, len );
			afc_string_delete ( buffer );
			*buffer_ptr = new_buffer;
		}
		else afc_string_add ( buffer, data, len );
	}

	return ( AFC_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_buffer_clear

       NAME: afc_xmlparser_buffer_clear - Clears a buffer

   SYNOPSIS: int afc_xmlparser_buffer_clear ( char ** buffer_ptr )

DESCRIPTION: clears a buffer created with afc_xmlparser_buffer_append().

     INPUTS: - buffer_ptr - pointer to the address of the buffer

    RESULTS: - AFC_ERR_NULL_POINTER if buffer_ptr is NULL

   SEE ALSO: - afc_xmlparser_buffer_append()
             - afc_xmlparser_buffer_delete()

@endnode
*/
int afc_xmlparser_buffer_clear ( char ** buffer_ptr )
{
	if ( buffer_ptr == NULL ) return AFC_LOG_FAST_INFO ( AFC_ERR_NULL_POINTER, "NULL buffer" );

	afc_string_clear ( *buffer_ptr );

	return ( AFC_ERR_NO_ERROR );
}

/*
@node afc_xmlparser_buffer_delete

       NAME: afc_xmlparser_buffer_delete - Deletes a buffer

   SYNOPSIS: int afc_xmlparser_buffer_delete ( char ** buffer_ptr )

DESCRIPTION: deletes a buffer created with afc_xmlparser_buffer_append().

     INPUTS: - buffer_ptr - pointer to the address of the buffer to be deleted.

    RESULTS: - AFC_ERR_NULL_POINTER if buffer_ptr is NULL

   SEE ALSO: - afc_xmlparser_buffer_append()
             - afc_xmlparser_buffer_clear()

@endnode
*/
int afc_xmlparser_buffer_delete ( char ** buffer_ptr )
{
	if ( buffer_ptr == NULL ) return AFC_LOG_FAST_INFO ( AFC_ERR_NULL_POINTER, "NULL buffer" );

	afc_string_delete ( *buffer_ptr );

	*buffer_ptr = NULL;

	return ( AFC_ERR_NO_ERROR );
}



/*******************
* Internal methods *
*******************/


/*
@node afc_xmlparser_internal_startElement

	         NAME: afc_xmlparser_internal_startElement - Calls the callback associated with a start tag

	     SYNOPSIS: void afc_xmlparser_internal_startElement ( void * xmlpstate, xmlChar * name, const xmlChar ** attrs )

	  DESCRIPTION: calls the callback associated with the starting tag of the element "name".
	               This procedure gets the pointer to the calling XmlParser instance from the
	               xmlpstate structure. If no callback is defined for the starting tag found, then
	               the procedure pointed to by the "tagUnknown" field is called: if the
	               latter is not defined, then noting is done.

	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.

	        INPUT: - xmlpstate - pointer to the structure containing the current state of the
	                 XmlParser (it's an instance of XmlParserState)
	               - name - name of the tag found
	               - attrs - pointer to an array of attributes of the starting tag (if any)

	      RESULTS: NONE.

	     SEE ALSO: afc_xmlparser_internal_endElement()

@endnode
*/
void afc_xmlparser_internal_startElement ( void * xmlpstate, xmlChar * name, const xmlChar ** attrs )
{
	XmlParserCallbackTuple * ct = NULL;
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	BeginCallbackFunc begCall;
	TagUnknownCallbackFunc tagunknown;

	afc_xmlparser_internal_attributes_parse ( state->xmlparser, attrs );

	if ( ( ct = ( XmlParserCallbackTuple * ) afc_dictionary_get ( state->xmlparser->callBacks, name ) ) == NULL ) {
		tagunknown = state->xmlparser->tagUnknown;
		if ( tagunknown != NULL ) tagunknown ( state->userdata, name, state->xmlparser->element_attrs );
		else return;
	}
	else {
		begCall = ct->beginCallback;
		if ( begCall != NULL ) begCall ( state->userdata, state->xmlparser->element_attrs );
	};
}

/*
@node afc_xmlparser_internal_endElement

	         NAME: afc_xmlparser_internal_endElement - Calls a callback associated with an ending tag

	     SYNOPSIS: void afc_xmlparser_internal_endElement ( void * xmlpstate, xmlChar * name )

	  DESCRIPTION: calls the callback associated with the ending tag of the element "name". If no
	               callback is defined for the ending tag found, then the procedure pointed to by
	               the "tagUnknown" field is called: if the latter is not defined then nothing
	               is done.

	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.

	        INPUT: - xmlpstate - pointer to an instance of XmlParserState containing the current state
	               of the parser
	               - name - name of the ending tag found

	      RESULTS: NONE.

	     SEE ALSO: afc_xmlparser_internal_startElement()

@endnode
*/
void afc_xmlparser_internal_endElement ( void * xmlpstate, xmlChar * name )
{
	XmlParserCallbackTuple * ct = NULL;
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	EndCallbackFunc endCall;
	TagUnknownCallbackFunc tagunknown;

	if ( ( ct = ( XmlParserCallbackTuple * ) afc_dictionary_get ( state->xmlparser->callBacks, name ) ) == NULL ) {
		tagunknown = state->xmlparser->tagUnknown;
		if ( tagunknown != NULL ) tagunknown ( state->userdata, name, state->xmlparser->element_attrs );
		else return;
	}
	else {
		endCall = ct->endCallback;
		if ( endCall != NULL ) endCall ( state->userdata );
	};
}

/*
@node afc_xmlparser_internal_startDocument

	         NAME: afc_xmlparser_internal_startDocument - Calls a callback at the beginning of the document

	     SYNOPSIS: void afc_xmlparser_internal_startDocument ( void * xmlpstate )

	  DESCRIPTION: calls the callback associated with the beginning of the document: if this
	               callback is not defined, then nothing is done.
	               The callback associated with the beginning of the document is often used to
	               perform some initializations.

	        INPUT: - xmlpstate - an instance of XmlParserState containing the current state of
	               the parser

	      RESULTS: NONE.

	     SEE ALSO: afc_xmlparser_internal_endDocument()

@endnode
*/
void afc_xmlparser_internal_startDocument ( void * xmlpstate )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * startDocument ) ( void * );
	//xmlParserCtxtPtr ctxt = ( xmlParserCtxtPtr ) state->xmlparser->sax_context;

	startDocument = state->xmlparser->startDocument;
	if ( startDocument != NULL ) startDocument ( state->userdata );
	else return;
}

/*
@node afc_xmlparser_internal_endDocument

	         NAME: afc_xmlparser_internal_endDocument - Calls a callback at the end of the document

	     SYNOPSIS: void afc_xmlparser_internal_endDocument ( void * xmlpstate )

	  DESCRIPTION: calls the callback associated with the end of the document: if this
	               callback is not defined, then nothing is done.
	               The callback associated with the end of the document is often used to
	               perform some finalizations.

	        INPUT: - xmlpstate - an instance of XmlParserState containing the current state of
	               the parser

	      RESULTS: NONE.

	     SEE ALSO: afc_xmlparser_internal_startDocument()

@endnode
*/
void afc_xmlparser_internal_endDocument ( void * xmlpstate )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * endDocument ) ( void * );

	endDocument = state->xmlparser->endDocument;
	if ( endDocument != NULL ) endDocument ( state->userdata );
	else return;
}

/*
@node afc_xmlparser_internal_characters

	         NAME: afc_xmlparser_internal_characters - Calls the callback for handling normal data

	     SYNOPSIS: void afc_xmlparser_internal_characters ( void * xmlpstate, const xmlChar * ch, int len )

	  DESCRIPTION: calls the callback used whenever "normal" characters are found: "normal" characters
	               are characters found outside any tag, i.e. characters not processed. If the
	               callback is not defined then nothing is done.
	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.
	               Important: the buffer "ch" is not necessarily NULL-terminated, so you must
	               be careful when processing it.

	        INPUT: - xmlpstate - an instance of XmlParserState containing the current state of the
	               parser
	               - ch  - the buffer containing the characters read
	               - len - number of characters contained in the buffer "ch"

	      RESULTS: NONE.

@endnode
*/
void afc_xmlparser_internal_characters ( void * xmlpstate, const xmlChar * ch, int len )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * characters ) ( void *, const xmlChar *, int );

	characters = state->xmlparser->characters;
	if ( characters != NULL ) characters ( state->userdata, ch, len );
	else return;
}

/*
@node afc_xmlparser_internal_entity_handler

	         NAME: afc_xmlparser_internal_entity_handler - Retrieves the content of an entity

	     SYNOPSIS: xmlEntityPtr afc_xmlparser_internal_entity_handler ( void * xmlpstate, const xmlChar * name )

	  DESCRIPTION: returns the content of an entity given its name. The result is a pointer to an
	               xmlEntity structure, defined in libxml. This function is never called directly:
	               it's called by the parser when it encounters an entity name in the parsed text.
	               The xmlChar type is defined in the libxml includes: it will be useful for example when
	               unicode support will be added.

	        INPUT: - xmlpstate - an instance of XmlParserState containing the current state of the
	                 parser
	               - name - name of the entity to search for

	      RESULTS: a pointer to an xmlEntity structure that holds all the information concerning
	               the entity (or NULL if not found).

@endnode
*/
xmlEntityPtr afc_xmlparser_internal_entity_handler ( void * xmlpstate, const xmlChar * name )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	xmlEntity * en = NULL;

	en = ( xmlEntity * ) afc_dictionary_get ( state->xmlparser->entities, ( char * ) name );
	if ( en == NULL ) en = xmlGetPredefinedEntity ( name );

	return ( en );
}


/*
void afc_xmlparser_internal_reference ( void * xmlpstate, const xmlChar * name )
{
	fprintf ( stderr, "entity: &%s;\n", name );
}

void afc_xmlparser_internal_entityDecl ( void * xmlpstate, const xmlChar * name, int type, const xmlChar * publicId, const xmlChar * systemId, xmlChar * content )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;

	fprintf ( stderr, "entity decl: name %s; type: %d; pubID: %s; sysID: %s; value: %s\n", name, type, publicId, systemId, content );
	fprintf ( stderr, "myDoc: %x\n", ( unsigned int ) state->xmlparser->sax_context );

	//if ( xmlAddDocEntity ( state->xmlparser->sax_context->myDoc, name, type, publicId, systemId, content) == NULL )
		//fprintf ( stderr, "something went wrong while adding the entity\n" );
}
*/


/*
@node afc_xmlparser_internal_entity_del

	         NAME: afc_xmlparser_internal_entity_del - Deletes an entity

	     SYNOPSIS: void afc_xmlparser_internal_entity_del ( xmlEntity * en )

	  DESCRIPTION: disposes of an entity

	        INPUT: - en - pointer to a valid xmlEntity structure

	      RESULTS: None

@endnode
*/
void afc_xmlparser_internal_entity_del ( xmlEntity * en )
{
	if ( en->name ) free ( ( xmlChar * ) en->name );
	if ( en->orig ) free ( ( xmlChar * ) en->orig );
	if ( en->content ) free ( ( xmlChar * ) en->content );
	if ( en->ExternalID) free ( ( xmlChar * ) en->ExternalID);
	if ( en->SystemID ) free ( ( xmlChar * ) en->SystemID );

	free ( en );
}


/*
@node afc_xmlparser_internal_warning

	         NAME: afc_xmlparser_internal_warning - Calls a callback when a warning is raised

	     SYNOPSIS: void afc_xmlparser_internal_warning ( void * xmlpstate, const char * msg )

	  DESCRIPTION: calls a callback when the parser raises a warning. If the callback has not been
	               set with a call to afc_xmlparser_set_callback() then the default behaviour is to
	               print the warning message to stderr.

	        INPUT: - xmlpstate - pointer to XmlParserState containing the current state of
	                 the parser
	               - msg - message describing the warning

	      RESULTS: NONE.

	     SEE ALSO: - afc_xmlparser_internal_error()
	               - afc_xmlparser_internal_fatal_error()

@endnode
*/
void afc_xmlparser_internal_warning ( void * xmlpstate, const char * msg )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * warning ) ( void *, const char * );

	warning = state->xmlparser->warning;
	if ( warning != NULL ) warning ( state->userdata, msg );
	else fprintf ( stderr, "warning: %s\n", msg );
}

/*
@node afc_xmlparser_internal_error

	         NAME: afc_xmlparser_internal_error - Calls a callback when an error is raised

	     SYNOPSIS: void afc_xmlparser_internal_error ( void * xmlpstate, const char * msg )

	  DESCRIPTION: calls a callback when the parser raises an error. If the callback has not been
	               set with a call to afc_xmlparser_set_callback() then the default behaviour is
	               to print the error message to stderr.

	        INPUT: - xmlpstate - pointer to XmlParserState containing the current state of 
	                the parser
	               - msg - message describing the error

	      RESULTS: NONE

	     SEE ALSO: - afc_xmlparser_internal_warning()
	               - afc_xmlparser_internal_fatal_error()

@endnode
*/
void afc_xmlparser_internal_error ( void * xmlpstate, const char * msg )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * error ) ( void *, const char * );

	error = state->xmlparser->error;
	if ( error != NULL ) error ( state->userdata, msg );
	else fprintf ( stderr, "error: %s\n", msg );
}

/*
@node afc_xmlparser_internal_fatal_error

	         NAME: afc_xmlparser_internal_fatal_error - Calls a callback when a fatal error is raised

	     SYNOPSIS: void afc_xmlparser_internal_fatal_error ( void * xmlpstate, const char * msg )

	  DESCRIPTION: calls a callback when the parser raises a fatal error. If the callback has not
	               been set with a call to afc_xmlparser_set_callback() then the default behaviour
	               is to print the error message to stderr.

	        INPUT: - xmlpstate - pointer to XmlParserState containing the current state of
	                 the parser
	               - msg - message describing the error

	      RESULTS: NONE

	     SEE ALSO: - afc_xmlparser_internal_error()
	               - afc_xmlparser_internal_warning()

@endnode
*/
void afc_xmlparser_internal_fatal_error ( void * xmlpstate, const char * msg )
{
	XmlParserState * state = ( XmlParserState * ) xmlpstate;
	void ( * fatal ) ( void *, const char * );

	fatal = state->xmlparser->fatalError;
	if ( fatal != NULL ) fatal ( state->userdata, msg );
	else fprintf ( stderr, "fatal error: %s\n", msg );
}

/*
       NAME: afc_xmlparser_internal_attributes_clear

   SYNOPSIS: int afc_xmlparser_internal_attributes_clear ( XmlParser * xmlparser )

DESCRIPTION: clears the internal dictionary used to store an element's attributes.

     INPUTS: - xmlparser - pointer to a valid XmlParser instance

    RESULTS: - AFC_ERR_NULL_POINTER if the xmlparser parameter is null
             - AFC_ERR_NO_ERROR otherwise

   SEE ALSO: - afc_xmlparser_internal_attributes_parse()

*/
int afc_xmlparser_internal_attributes_clear ( XmlParser * xmlparser )
{
	char * att_value = NULL;

	if ( xmlparser == NULL ) return AFC_LOG_FAST_INFO ( AFC_ERR_NULL_POINTER, "XmlParser NULL" );

	att_value = ( char * ) afc_dictionary_first ( xmlparser->element_attrs );
	while ( att_value != NULL ) {
		afc_string_delete ( att_value );
		att_value = ( char * ) afc_dictionary_next ( xmlparser->element_attrs );
	}

	afc_dictionary_clear ( xmlparser->element_attrs );

	return ( AFC_ERR_NO_ERROR );
}

/*
       NAME: afc_xmlparser_internal_attributes_parse

   SYNOPSIS: int afc_xmlparser_internal_attributes_parse ( XmlParser * xmlparser, const xmlChar ** attrs )

DESCRIPTION: examines the array of attributes generated by libxml2 functions and stores them
             in the internal "element_attrs" dictionary.

     INPUTS: - xmlparser - pointer to a valid XmlParser instance;
             - attrs - array of attribute name, value pairs (NULL terminated)

    RESULTS: - AFC_ERR_NULL_POINTER if xmlparser is NULL
             - AFC_ERR_NO_MEMORY if an attribute's name or value cannot be created
             - AFC_ERR_NO_ERROR if all is ok

   SEE ALSO: - afc_xmlparser_internal_attributes_clear()

*/
int afc_xmlparser_internal_attributes_parse ( XmlParser * xmlparser, const xmlChar ** attrs )
{
	int c = 0;
	char * att_name = NULL;
	char * att_value = NULL;

	if ( xmlparser == NULL ) return AFC_LOG_FAST_INFO ( AFC_ERR_NULL_POINTER, "XmlParser NULL" );

	afc_xmlparser_internal_attributes_clear ( xmlparser );

	if ( attrs != NULL ) {
		while ( attrs [ c ] != NULL ) {
			/* We have to copy the attribute name to a temporary string to perform the conversion
			   from xmlChar to char (libxml2 doesn't provide any function to do this :-(( ).
			   And afc_string_dup doesn't work for xmlChars because it's fooled about the actual length. */
			if ( ( att_name = afc_string_new ( xmlStrlen ( attrs [ c ] ) ) ) == NULL )
				return AFC_LOG_FAST_INFO ( AFC_ERR_NO_MEMORY, "Cannot allocate attribute name" );

			afc_string_copy ( att_name, attrs [ c ], xmlStrlen ( attrs [ c ] ) );

			c++;
			if ( ( att_value = afc_string_new ( xmlStrlen ( attrs [ c ] ) ) ) == NULL )
				return AFC_LOG_FAST_INFO ( AFC_ERR_NO_MEMORY, "Cannot allocate attribute value" );

			afc_string_copy ( att_value, attrs [ c ], xmlStrlen ( attrs [ c ] ) );

			afc_dictionary_set ( xmlparser->element_attrs, att_name, att_value );

			/* we dispose of the name, but NOT of the value, because afc_dictionary_set makes a copy of
			   the key but not of the value associated to the key. The value will be freed in the
			   afc_xmlparser_internal_attributes_clear function. */
			afc_string_delete ( att_name );
			c++;
		}
	}

	return ( AFC_ERR_NO_ERROR );
}



/* test */

#ifdef TEST_CLASS
struct miei_dati {
	char * buf;
};

void start_brano ( void * userdata, Dictionary * attrs )
{
	char * att_value = NULL;

	fprintf ( stdout , "inizio brano\n" );

	att_value = ( char * ) afc_dictionary_first ( attrs );
	while ( att_value != NULL ) {
		printf ( "  attribute name: %s\n", afc_dictionary_get_key ( attrs ) );
		printf ( "  attribute value: %s\n", att_value );
		att_value = ( char * ) afc_dictionary_next ( attrs );
	}
}

void end_brano ( void * userdata )
{
	fprintf ( stdout , "fine brano\n" );
}

void tagunknown ( void * userdata, const xmlChar * name,  Dictionary * attrs )
{
	fprintf ( stderr, "tag sconosciuto: <%s>\n", name );
}

void startdoc ( void * userdata )
{
	fprintf ( stdout, "Inizio documento\n" );
}

void enddoc ( void * userdata )
{
	fprintf ( stdout, "Fine documento\n" );
}

void handle_data ( void * userdata, const xmlChar * data, int len )
{
	struct miei_dati * dati = ( struct miei_dati * ) userdata;

	afc_xmlparser_buffer_append ( & dati->buf, data, len );
}

int main ( int argc, char * argv[] )
{
	int err = 0;
	struct miei_dati dati;
	XmlParser * xmlparser;
	AFC * afc = afc_new();
	
	xmlparser = afc_xmlparser_new();

	/* userdata initializations */
	dati.buf = NULL;

	if ( xmlparser == NULL ) 
	{
	  fprintf ( stderr, "Init of class XmlParser failed.\n" );
	  return ( 1 );
	}

	afc_xmlparser_add_callback ( xmlparser, "brano", start_brano, end_brano );
	afc_xmlparser_set_callback ( xmlparser, AFC_XMLPARSER_CBTYPE_TAG_UNKNOWN, tagunknown );
	afc_xmlparser_set_callback ( xmlparser, AFC_XMLPARSER_CBTYPE_START_DOC, startdoc );
	afc_xmlparser_set_callback ( xmlparser, AFC_XMLPARSER_CBTYPE_END_DOC, enddoc );
	afc_xmlparser_set_callback ( xmlparser, AFC_XMLPARSER_CBTYPE_CHARACTERS, handle_data );

	afc_xmlparser_entity_set ( xmlparser, "yyy", 0, NULL, NULL, "pippo");

	if ( ( err = afc_xmlparser_parse_file ( xmlparser, "fileXML.xml", &dati ) ) != AFC_XMLPARSER_ERR_NO_ERROR ) {
		fprintf ( stderr, "Well, something went wrong!\n" );
		exit ( 1 );
	}

	printf ( "----- document parsed -----\n" );
	printf ( "%s\n", dati.buf );
	printf ( "----- end of document -----\n" );

	afc_xmlparser_delete ( xmlparser );
	if ( dati.buf ) afc_xmlparser_buffer_delete ( & dati.buf );

	afc_delete ( afc );
	
	return ( 0 ); 
}
#endif
